﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Study.GoogleOptimizationTools.ConsoleApp.Examples
{
    public class ExampleLinearSolver
    {

     

        /// <summary>
        /// 有纸币 1, 2, 5, 10, 50, 100，从中任意可重复取出n张，凑总数为target范围内最优解
        /// 
        /// 以下流程：
        /// 1，定义变量
        /// 2，定义目标函数
        /// 3，定义约束
        /// 4，求解
        /// </summary>
        static void Example_1()
        {
            var source = new int[] { 1, 2, 5, 10, 50, 100 };
            var target = 51;

            //注意这里用线性求解器LinearSolver.Solve
            //求解过程中，变量的值只能是INTEGER，因此用 LinearSolver.Solver.CBC_MIXED_INTEGER_PROGRAMMING
            //假如是GLOP_LINEAR_PROGRAMMING,则x会取小数
            var solver = new Google.OrTools.LinearSolver.Solver("test", Google.OrTools.LinearSolver.Solver.CBC_MIXED_INTEGER_PROGRAMMING);

            //定义变量
            var x = solver.MakeIntVarArray(source.Length, 0, 3);

            //定义目标函数
            //这里的线性求解，固定形式为 w1*x1 + w2*x2 + wn*xn
            //因此，这里目标函数可以赋值每个x的权重w,注意下面的foreach
            var objective = solver.Objective();

            //SetCoefficient相当于设置每个x的权重w，因此这里使用币值作为w
            foreach (var item in x.Zip(source, (f, s) => new { count = f, amount = s }))
            {
                //x1*w1    x1=item.count,w1=item.amount
                objective.SetCoefficient(item.count, item.amount);
            }

            //设置 w1*x1 + w2*x2 + wn*xn 最大化
            objective.SetMaximization();

            //添加约束。
            //如果没有约束下，必然目标函数取最大值则会取最大金额 100 * 3
            //因此，添加约束为 X * { 1, 2, 5, 10, 50, 100 } <= 51
            //使求解器修改每个x的值的时候，受到限制
            var total = source.Zip(x, (f, s) => f * s).Aggregate((f, s) => f + s);
            solver.Add(total <= target);

            int count = solver.Solve();
            Console.WriteLine(count);

            string split = new string('=', 10);

            foreach (var item in x.Zip(source, (f, s) => new { count = f, amount = s }))
            {
                Console.WriteLine(split);
                Console.WriteLine($"{item.amount}x{item.count.SolutionValue()}={item.amount * item.count.SolutionValue()}");
            }

        }



    }
}
